package pers.cz.filter;

import pers.cz.pub.RespBean;
import pers.cz.session.ContextHolder;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwts;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.GenericFilterBean;
import pers.cz.session.User;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;

/**
 * @program: springSecurity
 * @description: JwtCheckFilter
 * @author: Cheng Zhi
 * @create: 2021-04-16 11:06
 **/
public class JwtCheckFilter extends GenericFilterBean {

    private static final String SIGNINGKEY = "PostGirl_Token_Public"; // 签名
    private static final String HEADERKEY = "authorization"; // header中token标识
    private static  List<String> anonymousRequests; // 允许匿名访问的路径不做拦截和校验
    static {
        anonymousRequests = new ArrayList<>();
        anonymousRequests.add("/profile/");
        anonymousRequests.add("/common/download");
        anonymousRequests.add("/common/download/resource");
        anonymousRequests.add("/swagger-ui.html");
        anonymousRequests.add("/swagger-resources/");
        anonymousRequests.add("/webjars/");
        anonymousRequests.add("/api-docs");
        anonymousRequests.add("/druid/");
        anonymousRequests.add("/static/");
        anonymousRequests.add("/websocket/");
        anonymousRequests.add("/api/test/");
        anonymousRequests.add("/api/verifyCodeController/");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

        HttpServletRequest req = (HttpServletRequest) servletRequest;
        String jwtToken = req.getHeader(HEADERKEY);
        final Enumeration<String> attributeNames = req.getAttributeNames();
        Jws<Claims> jws = null;
        String servletPath = req.getServletPath();
        for (String anonymousRequest : anonymousRequests) {

            if (servletPath.contains(anonymousRequest)) {
                filterChain.doFilter(servletRequest,servletResponse);
                return;
            }
        }
        try {
            jws = Jwts.parser().setSigningKey(SIGNINGKEY)
                    .parseClaimsJws(jwtToken.replace("Bearer", ""));
            Claims claims = jws.getBody();
            String usernames = claims.getSubject();
            servletRequest.setAttribute("userId",usernames);
            List<GrantedAuthority> authorities = AuthorityUtils.commaSeparatedStringToAuthorityList((String)claims.get("authorities"));
            UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(usernames, null, authorities);
            SecurityContextHolder.getContext().setAuthentication(token);
            // 这里初始化请求上下文容器
            ContextHolder.initContext();
            User userCustome = new User();
            userCustome.setUserId(Integer.valueOf(usernames));

            filterChain.doFilter(servletRequest,servletResponse);

        } catch (NullPointerException e) {

        } catch (ExpiredJwtException exception) {
            RespBean respBean = RespBean.error(exception.getMessage());
            respBean.setMsg("登录信息过期，请重新登录");
            respBean.setStatus(501); // 标识登录失败
            servletResponse.setContentType("application/json;charset=utf-8");
            PrintWriter out = servletResponse.getWriter();
            out.write(new ObjectMapper().writeValueAsString(respBean));
            out.flush();
            out.close();
        }


    }
}
